package fr.licpro.filebox.activity.fragments;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Fragment;
import android.content.ContentProviderOperation;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import ezvcard.Ezvcard;
import ezvcard.VCard;
import ezvcard.parameter.AddressType;
import ezvcard.parameter.EmailType;
import ezvcard.parameter.TelephoneType;
import ezvcard.property.Address;
import ezvcard.property.Email;
import ezvcard.property.Telephone;
import fr.licpro.filebox.R;
import fr.licpro.filebox.activity.ListActivity;
import fr.licpro.filebox.dto.enums.FileTypeEnum;
import fr.licpro.filebox.model.Content;
import fr.licpro.filebox.service.sync.FilesSync;
import fr.licpro.filebox.utils.FileCache;

/**
 * Fragment which contains the file detail
 * @author Abdessamad EL FATTAKE
 * @author Sylvain LE GOUELLEC
 */
public class DetailFragment extends Fragment {

	/**
	 * Static variables to describe the type of view
	 */
	private static int TEXT = 1;
	private static int IMAGE = 2;
	private static int WEBVIEW = 3;
	private static int VCARD = 4;
	
	/**
	 * VCard to display a contact
	 */
	private VCard mVcard;
	
	/**
	 * Content contains description of a file
	 */
	private Content mContent;
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		
		return inflater.inflate(R.layout.fragment_detail, container, false);
	}
	
	@Override
	public void onDestroyView() {
		((ListActivity)getActivity()).showVcardOptionMenu(false);
		getActivity().invalidateOptionsMenu();
		super.onDestroyView();
	}

	/**
	 * Adding a contact in the device from a VCARD
	 */
	public void addContact() 
	{
		if(mVcard != null)
		{
			String name = mVcard.getStructuredName().getGiven();
			String familyName = mVcard.getStructuredName().getFamily();
			List<Telephone> phones = mVcard.getTelephoneNumbers();
			List<Email> emails = mVcard.getEmails();
		    List<Address> addresses = mVcard.getAddresses();
			
			ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
			
			// Getting accounts on the device
			Account[] accounts = AccountManager.get(getActivity()).getAccounts();
			
			String accountName = "Local";
			String accountType = "fr.filebox";
			
			if(accounts.length > 0)
			{
				accountName = accounts[0].name;
				accountType = accounts[0].type;
			}
			
			/*
		     * Creates a new raw contact with its account type (server type) and account name
		     * (user's account). Remember that the display name is not stored in this row, but in a
		     * StructuredName data row. No other data is required.
		     */
		    ContentProviderOperation.Builder op =
		            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
		            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
		            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName);

		    // Builds the operation and adds it to the array of operations
		    ops.add(op.build());
		    
		    // Creates the display name for the new raw contact, as a StructuredName data row.
		    op =
		            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
		            /*
		             * withValueBackReference sets the value of the first argument to the value of
		             * the ContentProviderResult indexed by the second argument. In this particular
		             * call, the raw contact ID column of the StructuredName data row is set to the
		             * value of the result returned by the first operation, which is the one that
		             * actually adds the raw contact row.
		             */
		            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

		            // Sets the data row's MIME type to StructuredName
		            .withValue(ContactsContract.Data.MIMETYPE,
		                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

		            // Sets the data row's display name to the name in the UI.
		            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name + " " + familyName);

		    // Builds the operation and adds it to the array of operations
		    ops.add(op.build());
		    
		    for (Telephone phone : phones) 
		    {
		    	String phoneNumber = phone.getText();
		    	Set<TelephoneType> types = phone.getTypes();
		    	
		    	@SuppressWarnings("unused")
				boolean prefered = false;
		    	int phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM;

		    	for (TelephoneType type : types) 
		    	{
		    		if(type.equals(TelephoneType.PREF))
		    		{
		    			prefered = true;
		    		}
		    		else if(type.equals(TelephoneType.WORK))
		    		{
		    			phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_WORK;
		    		}
		    		else if(type.equals(TelephoneType.HOME))
		    		{
		    			phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_HOME;
		    		}
		    		else if(type.equals(TelephoneType.CELL))
		    		{
		    			phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE;
		    		}
				}
		    	
		    	// Inserts the specified phone number and type as a Phone data row
			    op =
			            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
			            /*
			             * Sets the value of the raw contact id column to the new raw contact ID returned
			             * by the first operation in the batch.
			             */
			            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

			            // Sets the data row's MIME type to Phone
			            .withValue(ContactsContract.Data.MIMETYPE,
			                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

			            // Sets the phone number and type
			            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber)
			            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);

			    // Builds the operation and adds it to the array of operations
			    ops.add(op.build());
			}
		    
		    for(Email email : emails)
		    {
		    	String emailValue = email.getValue();
		    	Set<EmailType> types = email.getTypes();
		    	
		    	@SuppressWarnings("unused")
				boolean prefered = false;
		    	int emailType = ContactsContract.CommonDataKinds.Email.TYPE_OTHER;
		    	
		    	for(EmailType type : types)
		    	{
		    		if(type.equals(EmailType.PREF))
		    		{
		    			prefered = true;
		    		}
		    		else if(type.equals(EmailType.HOME))
		    		{
		    			emailType = ContactsContract.CommonDataKinds.Email.TYPE_HOME;
		    		}
		    		else if(type.equals(EmailType.WORK))
		    		{
		    			emailType = ContactsContract.CommonDataKinds.Email.TYPE_WORK;
		    		}
		    	}
		    	
		    	// Inserts the specified email and type as a Phone data row
			    op =
			            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
			            /*
			             * Sets the value of the raw contact id column to the new raw contact ID returned
			             * by the first operation in the batch.
			             */
			            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

			            // Sets the data row's MIME type to Email
			            .withValue(ContactsContract.Data.MIMETYPE,
			                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

			            // Sets the email address and type
			            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, emailValue)
			            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);

			    /*
			     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
			     * will yield priority to other threads. Use after every set of operations that affect a
			     * single contact, to avoid degrading performance.
			     */
			    op.withYieldAllowed(true);

			    // Builds the operation and adds it to the array of operations
			    ops.add(op.build());
		    }
		    
		    for(Address address : addresses)
		    {
		    	String addressValue = buildAddress(address);
		    	Set<AddressType> types = address.getTypes();
		    	
		    	@SuppressWarnings("unused")
				boolean prefered = false;
		    	
		    	int addressType = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER;
		    	
		    	for(AddressType type : types)
		    	{
		    		if(type.equals(AddressType.PREF))
		    		{
		    			prefered = true;
		    		}
		    		else if(type.equals(AddressType.HOME))
		    		{
		    			addressType = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME;
		    		}
		    		else if(type.equals(AddressType.WORK))
		    		{
		    			addressType = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK;
		    		}
		    	}
		    	
		    	// Inserts the specified address and type as a Phone data row
			    op =
			            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
			            /*
			             * Sets the value of the raw contact id column to the new raw contact ID returned
			             * by the first operation in the batch.
			             */
			            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

			            // Sets the data row's MIME type to Address
			            .withValue(ContactsContract.Data.MIMETYPE,
			                    ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)

			            // Sets the address and type
			            .withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, addressValue)
			            .withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, addressType);

			    /*
			     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
			     * will yield priority to other threads. Use after every set of operations that affect a
			     * single contact, to avoid degrading performance.
			     */
			    op.withYieldAllowed(true);

			    // Builds the operation and adds it to the array of operations
			    ops.add(op.build());
		    }
		    
		    /*
		     * Applies the array of ContentProviderOperation objects in batch. The results are
		     * discarded.
		     */
		    try {
		    		getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
		    		
		    		Toast.makeText(getActivity(), R.string.contact_added, Toast.LENGTH_SHORT).show();
		    } 
		    catch (Exception e) {

		            // Display a warning
		            Context ctx = getActivity().getApplicationContext();

		            CharSequence txt = "Erreur creation contact";
		            int duration = Toast.LENGTH_SHORT;
		            Toast toast = Toast.makeText(ctx, txt, duration);
		            toast.show();
		    }
		}
	}

	@Override
	public void onViewCreated(View view, Bundle savedInstanceState) {

		Bundle args = getArguments();
		
		if(savedInstanceState != null && savedInstanceState.containsKey("CONTENT"))
		{
			mContent = savedInstanceState.getParcelable("CONTENT");
			
			setContent(mContent);
		}
		else if(args != null)
		{
			if(args.containsKey(FilesSync.FILE_CONTENT))
			{
				Content content = args.getParcelable(FilesSync.FILE_CONTENT);
				setContent(content);
			}
		}
		
		super.onViewCreated(view, savedInstanceState);
	}
	
	/**
	 * Sets the file content in the view
	 * @param content Object contains description of file
	 */
	public void setContent(Content content) 
	{
		mContent = content;
		
		FileCache cache = new FileCache(getActivity());
		
		File file = cache.getFile(content.getHashID());
		
		((ListActivity)getActivity()).showVcardOptionMenu(false);
		
		if(content.getMimeType().contains(FileTypeEnum.TEXT.getMimeType()))
		{
			hideOthersView(TEXT);
			loadTextFile(file);
		}
		else if(content.getMimeType().contains(FileTypeEnum.JPEG.getMimeType()) || content.getMimeType().contains(FileTypeEnum.PNG.getMimeType()))
		{
			hideOthersView(IMAGE);
			loadImageFile(file);
		}
		else if(content.getMimeType().contains(FileTypeEnum.VCARD.getMimeType()))
		{
			((ListActivity)getActivity()).showVcardOptionMenu(true);
			hideOthersView(VCARD);
			loadVcardFile(file);
		}
		else if(content.getMimeType().contains(FileTypeEnum.HTML.getMimeType()))
		{
			hideOthersView(WEBVIEW);
			loadHTMLFile(file);
		}
		
		getActivity().invalidateOptionsMenu();
	}
	
	/**
	 * Hides the other views which aren't concerned with the filetype content
	 * @param typeView View has to be show
	 */
	private void hideOthersView(int typeView) 
	{		
		if(typeView != TEXT)
		{
			TextView textView = (TextView) getView().findViewById(R.id.detail_textview);
			textView.setVisibility(View.GONE);
		}
		if(typeView != IMAGE)
		{
			ImageView imageView = (ImageView) getView().findViewById(R.id.detail_imageview);
			imageView.setVisibility(View.GONE);
		}
		if(typeView != VCARD)
		{
			LinearLayout layout = (LinearLayout) getView().findViewById(R.id.detail_vcard);
			
			layout.setVisibility(View.GONE);
		}
		if(typeView != WEBVIEW)
		{
			LinearLayout layoutWebView = (LinearLayout) getView().findViewById(R.id.layout_webview);
			layoutWebView.setVisibility(View.GONE);
		}
	}

	/**
	 * Loading and shows the HTML content
	 * @param file HTML file
	 */
	private void loadHTMLFile(File file) {

		LinearLayout layoutWebView = (LinearLayout) getView().findViewById(R.id.layout_webview);
		layoutWebView.setVisibility(View.VISIBLE);
		
		final ProgressBar progressBar = (ProgressBar) layoutWebView.findViewById(R.id.progess_bar);
		
		final WebView webView = (WebView) getView().findViewById(R.id.detail_webview);
		webView.setWebViewClient(new WebViewClient(){
			@Override
			public boolean shouldOverrideUrlLoading(WebView view, String url) {
			
				return false;
			}
		});
		
		webView.getSettings().setJavaScriptEnabled(true);
		
		webView.setWebChromeClient(new WebChromeClient(){
			
			@Override
			public void onProgressChanged(WebView view, int newProgress) 
			{
				if(newProgress < 100 && progressBar.getVisibility() == ProgressBar.GONE){
                    progressBar.setVisibility(ProgressBar.VISIBLE);
                }
				progressBar.setProgress(newProgress);
                if(newProgress == 100) {
                	progressBar.setVisibility(ProgressBar.GONE);
                }
			}
		});
		
		/* Button Previous and Next */
		
		Button btnPrevious = (Button)getView().findViewById(R.id.btn_prec_webview);
		Button btnNext = (Button)getView().findViewById(R.id.btn_suiv_webview);
		
		btnPrevious.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				if(webView.canGoBack())
					webView.goBack();
			}
		});
		
		btnNext.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				if(webView.canGoForward())
					webView.goForward();
			}
		});		
		
		try 
		{
			String data = readFile(file);
			webView.loadData(data, "text/html", null);
			
		} 
		catch (IOException e) 
		{
			Toast.makeText(getActivity(), R.string.error_occured, Toast.LENGTH_SHORT).show();
		}
		
	}

	/**
	 * Reads the file content
	 * @param file The file has to be read
	 * @return The string content
	 * @throws IOException
	 */
	private String readFile(File file) throws IOException {
		
		BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
			
		String line = null;
			
		StringBuilder builder = new StringBuilder();
			
		while ((line = reader.readLine()) != null) 
		{
			builder.append(line).append(System.getProperty("line.separator"));
		}
		
		reader.close();
		
		return builder.toString();
	}

	/**
	 * Loading and shows a VCARD file
	 * We don't show all informations (but all informations are added in the device)
	 * @param file VCARD file
	 */
	private void loadVcardFile(File file) 
	{
		LinearLayout layout = (LinearLayout) getView().findViewById(R.id.detail_vcard);
		TextView vcardPrenom = (TextView) getView().findViewById(R.id.vcard_prenom);
		TextView vcardNom = (TextView) getView().findViewById(R.id.vcard_nom);
		TextView vcardTel = (TextView) getView().findViewById(R.id.vcard_tels);
		TextView vcardMail = (TextView) getView().findViewById(R.id.vcard_mail);
		TextView vcardAdresse = (TextView) getView().findViewById(R.id.vcard_adresse);
		
		layout.setVisibility(View.VISIBLE);
		
		try 
		{
			String data = readFile(file);
			
			mVcard = Ezvcard.parse(data).first();
			
			vcardPrenom.setText(mVcard.getStructuredName().getGiven());
			vcardNom.setText(mVcard.getStructuredName().getFamily());
			vcardTel.setText(mVcard.getTelephoneNumbers().get(0).getText());
			
			if(!mVcard.getEmails().isEmpty())
			{
				vcardMail.setText(mVcard.getEmails().get(0).getValue());
			}
			else
			{
				vcardMail.setVisibility(View.GONE);
			}
			
			
			if(!mVcard.getAddresses().isEmpty())
			{
				Address addressValue = mVcard.getAddresses().get(0);
				
				vcardAdresse.setText(buildAddress(addressValue));
			}
			else
			{
				vcardAdresse.setVisibility(View.GONE);
			}
			
		} 
		catch (IOException e) 
		{
			Toast.makeText(getActivity(), R.string.error_occured, Toast.LENGTH_SHORT).show();
		}
	}

	/**
	 * Laoding and shows an Image file
	 * @param file Image file
	 */
	private void loadImageFile(File file) 
	{		
		ImageView imageView = (ImageView) getView().findViewById(R.id.detail_imageview);
		imageView.setVisibility(View.VISIBLE);
		
		try 
		{
			byte[] data = readBinaryData(file);
			
			Bitmap image = BitmapFactory.decodeByteArray(data, 0, data.length);
			
			imageView.setImageBitmap(image);
			
		} 
		catch (IOException e)
		{ 
			Toast.makeText(getActivity(), R.string.error_occured, Toast.LENGTH_SHORT).show();
		}
	}

	/**
	 * Read binary data
	 * @param file Content file
	 * @return The byte data
	 * @throws IOException
	 */
	private byte[] readBinaryData(File file) throws IOException 
	{
		BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file));
		long fileSize = file.length();
		
		byte[] ret = new byte[(int) fileSize];
		stream.read(ret);
		
		stream.close();
		return ret;
	}

	/**
	 * Loading and shows a text file
	 * @param file Text file
	 */
	private void loadTextFile(File file)
	{
		TextView textView = (TextView) getView().findViewById(R.id.detail_textview);
		
		textView.setVisibility(View.VISIBLE);
		
		try
		{
			String data = readFile(file);
			textView.setText(data);
			
		} 
		catch (IOException e) 
		{ 
			Toast.makeText(getActivity(), R.string.error_occured, Toast.LENGTH_SHORT).show();
		}
	}
	
	/**
	 * Builds String formatted address
	 * @param address VCARD address
	 * @return The string
	 */
	private String buildAddress(Address address)
	{
		StringBuilder ad_builder = new StringBuilder();
		ad_builder.append(address.getStreetAddress()).append(" ").append(address.getPostalCode()).append(" ").
			append(address.getLocality()).append(" ").append(address.getCountry());
		
		return ad_builder.toString();
	}
	
	@Override
	public void onSaveInstanceState(Bundle outState) 
	{
		outState.putParcelable("CONTENT", mContent);
	
		super.onSaveInstanceState(outState);
	}
}
